/*
 * Video.c
 *
 *  Created on: Sep 4, 2011
 *      Author: Patrick
 */

#include "Video.h"
#include <sys/time.h>

struct MCSDLVideoContext mcsdlVideoContext;
libvlc_instance_t *vlcInstance;
libvlc_media_t *vlcMedia;
libvlc_media_player_t *vlcMediaPlayer;
struct timeval currentTime;
long nextFrameStream;

void MCVideoCreate(char* mediaPath, int width, int height) {

	if (mcsdlVideoContext.surface != NULL) {
		MCVideoDestroy();
	}

	mcsdlVideoContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 16, 0x001f, 0x07e0, 0xf800, 0);
	mcsdlVideoContext.mutex = SDL_CreateMutex();

	const char *vlc_argv[] = { "--plugin-path=./plugins", "--no-video-title-show" };
	int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);

	vlcInstance = libvlc_new(vlc_argc, vlc_argv);
	vlcMedia = libvlc_media_new_path(vlcInstance, mediaPath);
	vlcMediaPlayer = libvlc_media_player_new_from_media(vlcMedia);

	libvlc_media_release(vlcMedia);
	libvlc_video_set_callbacks(vlcMediaPlayer, MCSDLVideoLock, MCSDLVideoUnlock, MCSDLVideoDisplay, &mcsdlVideoContext);
	libvlc_video_set_format(vlcMediaPlayer, "RV16", mcsdlVideoContext.surface->w, mcsdlVideoContext.surface->h, mcsdlVideoContext.surface->w * 2);

	gettimeofday(&currentTime, NULL);
	nextFrameStream = currentTime.tv_sec * 1000 + currentTime.tv_usec / 1000;

}

void MCVideoPlay() {
	if (libvlc_media_player_get_state(vlcMediaPlayer) != libvlc_Playing) {
		libvlc_media_player_play(vlcMediaPlayer);
	}
}

void MCVideoPause() {
	if (libvlc_media_player_get_state(vlcMediaPlayer) == libvlc_Playing) {
		libvlc_media_player_pause(vlcMediaPlayer);
	}
}

int MCVideoIsPlaying() {
	return libvlc_media_player_is_playing(vlcMediaPlayer);
}

float MCVideoGetDuration() {
	return libvlc_media_player_get_length(vlcMediaPlayer);
}

float MCVideoGetPosition() {
	return libvlc_media_player_get_position(vlcMediaPlayer);
}

void MCVideoSetPosition(float position) {
	libvlc_media_player_set_position(vlcMediaPlayer, position);
}

GLuint MCVideoStreamTexture(GLuint texture) {

	gettimeofday(&currentTime, NULL);
	long timeInMillis = currentTime.tv_sec * 1000 + currentTime.tv_usec / 1000;

	if (MCVideoIsPlaying() && timeInMillis > nextFrameStream) {

		int w = mcsdlVideoContext.surface->w;
		int h = mcsdlVideoContext.surface->h;
		void * rawData = (void *)malloc(w * h * 4);
		Uint8 * pixelSource;
		Uint8 * pixelDestination = (Uint8 *)rawData;
		Uint32 pix;
		unsigned int i, j;

		SDL_LockMutex(mcsdlVideoContext.mutex);
		SDL_LockSurface(mcsdlVideoContext.surface);

		for (i = h; i > 0; i--) {
			for (j = 0; j < w; j++) {
				pixelSource = (Uint8 *)mcsdlVideoContext.surface->pixels + (i - 1) * mcsdlVideoContext.surface->pitch + j * 2;
				pix = *(Uint16 *)pixelSource;
				SDL_GetRGBA(pix, mcsdlVideoContext.surface->format, &(pixelDestination[0]), &(pixelDestination[1]), &(pixelDestination[2]), &(pixelDestination[3]));
				pixelDestination += 4;
			}
		}

		SDL_UnlockSurface(mcsdlVideoContext.surface);
		SDL_UnlockMutex(mcsdlVideoContext.mutex);

		// Building the texture
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (Uint8 *)rawData);

		free(rawData);

		// Calculate the next time we should stream a frame.  Typically this will be the current time in milliseconds +
		// the framerate of the video itself (in frames per millisecond).  If for some reason VLC won't give us the
		// framerate (and it defaults to 0) we'll just render a lot more-- no biggie.
		nextFrameStream = timeInMillis + libvlc_media_player_get_fps(vlcMediaPlayer) * 0.9;

	}

	return texture;

}

void MCVideoDestroy() {
	libvlc_media_player_stop(vlcMediaPlayer);
	libvlc_media_player_release(vlcMediaPlayer);
	libvlc_release(vlcInstance);
	SDL_DestroyMutex(mcsdlVideoContext.mutex);
	SDL_FreeSurface(mcsdlVideoContext.surface);
}

void *MCSDLVideoLock(void *data, void **p_pixels) {
	SDL_LockMutex(mcsdlVideoContext.mutex);
	SDL_LockSurface(mcsdlVideoContext.surface);
	*p_pixels = mcsdlVideoContext.surface->pixels;
	return NULL;
}

void MCSDLVideoUnlock(void *data, void *id, void * const *p_pixels) {
	SDL_UnlockSurface(mcsdlVideoContext.surface);
	SDL_UnlockMutex(mcsdlVideoContext.mutex);
}

void MCSDLVideoDisplay(void *data, void *id) {
	/* VLC wants to display the video */
}

